home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PsL Monthly 1993 December
/
PSL Monthly Shareware CD-ROM (December 1993).iso
/
prgmming
/
dos
/
c
/
cstream.exe
/
CSTREAM.DOC
< prev
next >
Wrap
Text File
|
1991-06-07
|
11KB
|
363 lines
Dear C++ programmer,
Thank you for downloading csteam. The StreamableClass class
provides a base class for a polymorphic cluster of
streamable classes! In other words, any class derived from
the StreamableClass can be stored on a stream and
subsequently loaded from the stream later. All such classes
can be stored in any order and/or mix on the same stream.
The inspiration for my StreamableClass comes from Borland's
TurboVision for Turbo Pascal. A TurboVision for BC++ has
been hinted at by Borland but cstream allows you to stream
classes NOW without the whole TurboVision tool. Not to
mention it's FreeWare.
Getting Started
===============
Copy cstream.hpp to your header directory and cstream.cpp
along with csdemo.cpp to your source directory. Compile
csdemo.cpp and cstream.cpp and link (a project file is
included, i.e. csdemo.prj). Run this demo and study its
source.
In order to make a class streamable, derive it from
StreamableClass either publicly or privately.
class Employee : public StreamableClass {
...
public:
StreamableClassID(Employee,StreamableClass,2);
...
Employee();
~Employee();
};
The StreamableClassID macro is used to assign a unique id by
which the class can be identified on the stream. This macro
also declares several required member functions of which you
must define store() and load(). Below is the macro
definition as it appears in cstream.hpp.
#define StreamableClassID(class, base, id) \
public: \
enum { CLASS_ID = id }; \
protected: \
base :: setID; \
class (unsigned cid = CLASS_ID) : base (cid) \
{ return; } \
public: \
base :: ID; \
operator StreamC() { return (StreamC)this; } \
virtual void store(ostream& os); \
static StreamC load(istream& is, StreamC C)
Your store function should write out to "os" the data
members of your class. Don't worry about the id or any
fields in StreamableClass - this is handled automatically.
void Employee::store(ostream& os)
{
os << name << endf << address << endf
<< cityStateZip << endf;
}
The endf manipulator outputs the end of field terminator
which is defined as:
char StreamableClass::FieldTermChar = '\n';
If you decide to change it, make sure it isn't a character
that can be interpreted as part of a number!
Your load function should read in the data stored in the
extact order it was written out.
StreamC Employee::load(istream& is, StreamC C)
{
if (!C) if ((C = (StreamC) new Employee())
== StreamC0)
return C;
is.getline(nameBuf,MAX_STR_BUF,
StreamableClass::FieldTermChar);
is.getline(addressBuf,MAX_STR_BUF,
StreamableClass::FieldTermChar);
is.getline(cityStateZipBuf,MAX_STR_BUF
,StreamableClass::FieldTermChar);
((EmployeE)C)->construct(nameBuf,addressBuf,
cityStateZipBuf);
return C;
}
If the load() function is called to load a class of this
type, then C is NULL and load() calls the default
constructor for your class as defined in the
StreamableClassID() macro above. This is used simply to
satisfy the new operator and to set the ID of the class.
Load() can also be called from a derived class in which case
C is not NULL! Your load function should procedure to read
the data in, in the order stored by your store() function.
To be consistent, all loads call the protected member
function, construct(), which you must write, with the data
read from the stream. Your class' regular constructor
should typically call construct() to do the nonstream
construction of your class. This way everybody's code using
streamable classes will read the same.
The trick to streamable classes is in the load() function.
It is a static member of StreamableClass and thus has no
implicit "this" parameter. The address of a static function
can be taken where as the address of a constructor cannot
be! This is why load() is declared as such. The design of
streamable classes in C++ requires that we call a
constructor - how else can we insure a portable way of
initializing virtual function tables?
Note also that load() returns "StreamC", a pointer to a
StreamableClass base. All we know when loading a
StreamableClass is that it is a StreamableClass. You must
call the ID() member function, which returns the unique id
you defined in the StreamableClassID() macro, to determine
its class.
If your class is not directly derived from StreamableClass,
it still needs to use the StreamableClassID() macro.
class Special : Product {
...
void construct(...);
...
public:
StreamableClassID(Special,Product,4);
...
};
Your store() function should call the store() function of
its immediate base class before storing its own data
members!
void Special::store(ostream& os)
{
Product::store(os);
os << specialPrice << endf;
}
Likewise your load() function should call the load()
function of its immediate base class before loading its own
data members!
StreamC Special::load(istream& is, StreamC C)
{
if (!C) if ((C = (StreamC) new Special())
== StreamC0)
return C;
/***/ Product::load(is,C); /****/
is >> spriceBuf;
is.get(); // field term char
((SpeciaL)C)->construct(spriceBuf);
return C;
}
You should be wondering at this point how the correct static
load() function will be called. We need to back up first
and see how to initialize the whole class-stream system.
This is done by a global call to the macro
StreamableClasses().
StreamableClasses(20);
main()
{
RegisterClass(Employee);
RegisterClass(Product);
RegisterClass(Special);
...
StreamableClass *S1, *S2, *S3;
ifstream iS("emp$prod.cls");
iS >> S1 >> S2 >> S3;
//S1->ID()
}
The call here initializes the system to hold a maximum of 20
records for different streamable classes in our application.
You should set your count appropriately.
The classes to be streamed must be registered before
attempting to stream them with a call to the macro,
RegisterClass(). The RegisterClass() macro registers your
class' ID and load function.
When you go to read back in classes from a stream, define
some pointers to StreamableClass and then fetch the classes
from the stream to these pointers. To determine the actual
class fetched, call the ID() member function.
Registration
============
If you find cstream useful and are using it in your
applications, tell your friends. You don't need to register
cstream -- it's freeware! All I ask is that you leave my
copyright notice intact and to give credit where credit is
due! How about dropping me a line (postcard or email) -- I
always like to here your comments and suggestions. It's
also nice to get alittle feedback and know that somebody is
really out there!
Other Freeware Goodies available for Borland C++
=======================================================
pckey PC keyboard class w/auto enhanced
gconio Graphics mode conio class
cmouse Mouse Driver class w/ intr handler
cmdln Command line parser class
And Shareware
=============
FlexList II ANSI && K&R C
FlexList II C++
* Ready-to-run C/C++ linked lists.
* Hybrid structure: stack,queue,list,array.
* Stores Heterogeneous/homogeneous data
* Eliminates the need for parameterized list
templates such as are proposed for
C++ in the future.
Loose Data Binder C++